/*
 * GeoVISTA Center (Penn State, Dept. of Geography)
 * Copyright (c), 2003-2011,  Jin Chen and Alan M. MacEachren, GeoVISTA Center, Penn State University
 * Licensed under Simplified BSD License
 *
 *  
 *  
 * !!  value at (x,y) = value at (y,x)
 * @author: jin Chen
 * @date: May 29, 2005$
 * @version: 1.0
 */
package edu.psu.geovista.app.hce.clustering.matrix;



import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class DfDialogMatrixFloat implements DialogMatrixFloat {
    private static final boolean DEBUG=true;
    protected List rows;//=new ArrayList();// 
   // List rowProperties;//=new ArrayList();  // 
    protected List  rownames; // 
    public DfDialogMatrixFloat(List matrixData) {
        this.rows = matrixData;
    }

    public DfDialogMatrixFloat(int size) {
        init(size);

    }

    protected void init(int size){
        rows=new ArrayList(size);
        rownames=new ArrayList<String>(size);
        //rowProperties=new ArrayList(size);
        // Initialize the matrix. ith element has length = i+1 ( i start with 0)
        for (int i=0;i<size;i++){
            float[] row=new float[i+1];
            rows.add(row);
            rownames.add("row "+i);
        }

    }
    public void setRowName(int index, String name){
        rownames.set(index,name);
    }
    public void setRowNames(List rownames){
        this.rownames.clear();
        this.rownames.addAll(rownames);
    }

    public List getRownames() {
        return rownames;
    }
    public int getIndex(Object name){
        for (int i=0;i<rownames.size() ;i++){
            Object aname = rownames.get(i);
            if(aname.equals(name) ){
                return i;
            }
        }
        return -1;
    }

    /**
     *  
     * @param x
     * @param y
     * @param value   
     */
    public void set(int x, int y, float value){
        if(x>this.size()||y>=size()){
           new IllegalArgumentException(this.getClass().getName() +" is passed a index("+x+","+y+") which exceeds the size "+size());
        }
        MatrixIndex index=(getMatrixIndex(x,y));
        //System.out.println("set ("+index.x+","+index.y+")");
        float[] arow = (float[]) rows.get(index.x);
        arow[index.y]=value;
    }
    /**
     *  
     * @param x
     * @param y
     * @return
     */
    public float get(int x, int y){
        MatrixIndex index=(getMatrixIndex(x,y));
        float[] arow = (float[]) rows.get(index.x);
        return  arow[index.y];
    }

    /**
     *
     * @param row
     * @return     a row of similarity: a similarity btw an observation and each of all other observations
     */
    public float[] getRowSimilarity(int row){
        int columncount = this.rows.size();
        float[] sims=new float[columncount];
        for (int i=0;i<columncount;i++){
            sims[i]=this.get(row,i);
        }
        return sims;

    }
     public float[] getRowSimilarity(Object  name){
         int index = this.getIndex(name);
         if(index<0)return new float[0];
         else{
             return  getRowSimilarity(index);
         }
     }

     /**
     *  
     * @param indexs
     */
    public void delete(int indexs[]){
       Arrays.sort(indexs);
        // 
       for (int i=indexs.length -1;i>=0;i--){
           delete(indexs[i]);
       }
    }
    /**
     *  
     * @param index
     */
    protected  void delete(int index){


        // 
        for (int i=index+1;i<rows.size() ;i++){
              float[] arow = (float[]) rows.get(i);
              float[] newrow =remove(arow,index);// 
              rows.set(i,newrow);
        }
        // 
        rows.remove(index);

    }
    public static float[]  remove(float[] array,int index ){
          RangeCheck(array,index);
          if (array.length <=1){
              return new float[0];
          }

          float[] newarray=new float[array.length -1];


          System.arraycopy(array,0,newarray,0,index);
          System.arraycopy(array,index+1,newarray,index,array.length-index-1);
          return newarray;


      }
       private static void RangeCheck(float[] array,int index) {
        if (index >= array.length )
            throw new IndexOutOfBoundsException(
            "Index: "+index+", Size: "+array.length);
     }

    /**
     *  
     * @param row
     */
    public void add(float[] row){
        if(row.length !=size()+1){
           new IllegalArgumentException(this.getClass().getName() +" fail to add an array which length !="+(size()+1));

        }

        rows.add(row);// 
    }
    public int size(){
        return rows.size() ;
    }

    /**
     *  
     * @return   point.x, point.y is the location of the cell in the matrix
     */
    public Point findMin2(){
       float[] rowMinis=new float[rows.size()];// 
       for (int i=0;i<rows.size() ;i++){
           float[] arow = (float[]) rows.get(i);

           float[] smallest = findSmallest(arow);
           rowMinis[i]=smallest[1];
       }

        float[] smallest = findSmallest(rowMinis);

        int row = (int) smallest[0];
        float smallestvalue=smallest[1];

        float[] arow = (float[]) rows.get(row);
        smallest = findSmallest(arow);
        int col=(int) smallest[0];

        return new Point(row,col);


    }
    public static float[] findSmallest(float[] data){
        int index=findAValid(data);
         if(index<0){ //all data are NaN
             return new float[]{0,Float.NaN};
         }
         float small=data[index];

         for (int i=0;i<data.length ;i++){
              if(Double.isNaN(data[i]))continue;
             if(data[i]<small){
                small=data[i];
                index=i;
             }
         }
         return new float[]{index,small};
     }
     public static int findAValid(float[] data){
        for (int i=0;i<data.length ;i++){
           if(!Double.isNaN(data[i]))return i;
        }
        return -1;
    }
    public Point findMin(){
       //return new Point(0,0);
       //float[] rowMaxs=new float[rows.size()];// 
       List extremes=new ArrayList(rows.size()-1);
       for (int i=1;i<rows.size() ;i++){// 
           float[] arow = (float[]) rows.get(i);
           Extreme e = findSmallestE(arow );
           e.x =(short) i;//ith row
           extremes.add(e);
       }
        Collections.sort(extremes);
        Extreme target = (Extreme) extremes.get(0);

        return new Point(target.x,target.y);
    }

    /**
     * //todo need make it reliable
     *  
     * @return
     */
     public Point findMax(){
       //return new Point(0,0);
       //float[] rowMaxs=new float[rows.size()];// 
       if(rows.size()==1){
           Point point = new Point(-1,-1);
       }
       List extremes=new ArrayList(rows.size()-1);
       for (int i=1;i<rows.size() ;i++){// 
           float[] arow = (float[]) rows.get(i);
           Extreme e = findLargestE(arow );//todo ,i//find largest of a row     //change from 1 to i make big difference
           e.x =(short) i;//ith row
           extremes.add(e);
       }
        if(extremes.size() <=0){
            return   new Point(-1,-1);
            //System.out.println("");
        }
        Collections.sort(extremes);

        Extreme largest = (Extreme) extremes.get(extremes.size()-1 );

        return new Point(largest.x,largest.y);
    }
     /**
     * Assume all data are valid ( not a NaN)
     * @param data
     * @return
     */
   private Extreme findSmallestE(float[] data){
        short index=0;
        float target=data[index];

        for (short i=0;i<data.length-1 ;i++){// 
              if(data[i]<target){

                target=data[i];
                index=i;
            }
        }
        Extreme e=new Extreme();
        e.value =target;
        e.y =index;
        return e;
    }
    /**
     * Assume all data are valid ( not a NaN)
     * @param data
     * @return
     */
    private Extreme findLargestE(float[] data){
        short index=0;
        float large=data[index];

        for (short i=0;i<data.length-1 ;i++){// 
              if(data[i]>large){

                large=data[i];
                index=i;
            }
        }
        Extreme e=new Extreme();
        e.value =large;
        e.y =index;
        return e;
    }
       public static float[] findLargest(float[] data){
         int index=findAValid(data);
         if(index<0){ //all data are NaN
             return new float[]{0,Float.NaN};
         }
         float large=data[index];

         for (int i=0;i<data.length ;i++){
             if(Double.isNaN(data[i]))continue;

             if(data[i]>large){

                large=data[i];
                index=i;
             }
         }
         return new float[]{index,large};
     }
    public Point findMax2(){
       //return new Point(0,0);
       float[] rowMaxs=new float[rows.size()];// 

       for (int i=0;i<rows.size() ;i++){
           float[] arow = (float[]) rows.get(i);
           float[] largest = findLargest(arow );//todo ,i//find largest of a row     //change from 1 to i make big difference
           rowMaxs[i]=largest[1];
       }
        rowMaxs[0]=Float.NaN ;
        float[] laregest = findLargest(rowMaxs); //find largest among largest values of the rows

        int row = (int) laregest[0];
        ///float largestvalue=laregest[1];

        float[] arow = (float[]) rows.get(row);
        laregest = findLargest(arow);  //todo ,arow.length -1
        int col=(int) laregest[0];

        return new Point(row,col);   
    }
   /*  public static float[] findLargest(float[] data, int ignore){
         int index=findAValid(data);
         if(index<0){ //all data are NaN
             return new float[]{0,Float.NaN};
         }
         float large=data[index];

         for (int i=0;i<data.length ;i++){
              if(Double.isNaN(data[i]))continue;
             if(i!=ignore&&data[i]>large){
                large=data[i];
                index=i;
             }
         }
         return new float[]{index,large};
     }
  */
    public List getMatrixData(){
        return this.rows ;
    }
    public void setMatrixData (List matrixdata){
        this.rows =matrixdata;
    }
     public void clone(DfDialogMatrixFloat matrix){
         List matrixData = matrix.getMatrixData();
         this.setMatrixData(matrixData);
     }

    public void print(){
       if(DEBUG){
       System.out.println("Print a dialog matrix");
       for (int i=0;i<rows.size() ;i++){
              float[] arow = (float[]) rows.get(i);
              System.out.print("row:"+i+" ");
              print(arow);

        }
       }

    }


    /*******************************************************************************************************
     *                static
     *******************************************************************************************************/

    public static void print(float[] array){
        for (int i=0;i<array.length ;i++){
            System.out.print(array[i]+", ");
        }
        System.out.println("\r");
    }

    public static DfDialogMatrixFloat createSample(int size){
        DfDialogMatrixFloat matrix=new DfDialogMatrixFloat(size);
        for (int i=0;i<size;i++){
            float[] arow=createArray(i+1);
            for (int j=0;j<arow.length ;j++){
                matrix.set(i,j,arow[j]);
            }

        }
        return matrix;
    }

    public  static float[] createArray(int size){
        float[] array=new float[size];
        for (int i=0;i<size;i++){
            array[i]=i+1;

        }
        return array;

    }

    public static  MatrixIndex getMatrixIndex( int x, int y){
        // int row,col=0;
        MatrixIndex index=new MatrixIndex();
        if (x>=y){
            index.x=x;
            index.y=y;
        }
        else{
            index.x=y;
            index.y=x;
        }
        return index;
    }

    static class MatrixIndex{
        int x; //row
        int y; //column
    }

}
